package com.koylu.faces.security;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;

import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.digester.Digester;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.koylu.faces.security.user.User;
import com.koylu.faces.security.user.UserManager;

public class AccessManager implements PhaseListener{
	
	private static final String CONFIG               = "access";
	private static final String USER_MANAGER         = "userManager";
	private static final String DEFAULT_USER_MANAGER = "com.koylu.faces.security.user.UserManagerImpl";
	private static UserManager  userManager          = null;
	private static Log          logger               = null;
	private static Map          accessMap            = new HashMap();
	private static Access       access               = null;
	private static final String RIGHTS               = "_rights_";
	
	public AccessManager() {
		logger = LogFactory.getLog(getClass());
	}
	
	public void afterPhase(PhaseEvent event) {}

	public void beforePhase(PhaseEvent event) {
		if( access == null){			
			initialize(event.getFacesContext());			
		}
		FacesContext facesContext = event.getFacesContext(); 
		String view = facesContext.getViewRoot().getViewId();
		logger.info("view-->"+view);
		User user = userManager.getUser();
		logger.info("user-->"+user);
		AccessRule rule = findRule(view);
		logger.info("rule-->"+rule);
		if(rule != null){
			if(rule.isPrivate()){						
				if(rule.checkGroups( user )){ //add Rights to request
					logger.info("add rights to request");
					((HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()).setAttribute(RIGHTS,rule.getAccessRights(user));	
				}else{ // goto error page
					logger.info("goto error page");
					FacesContext.getCurrentInstance().getViewRoot().setViewId(AccessManager.access.getError());
				}
			}		
		}
	}
	
	private AccessRule findRule(String view){
		if(view == null){
			return null;
		}
		AccessRule rule = (AccessRule)accessMap.get(view);
		if(rule != null){			
			return rule;
		}
		for(int k=0;k<access.getRules().size();k++){
			rule = (AccessRule)access.getRules().get(k);
			if(rule.check(view)){				
				accessMap.put(view,rule);				
				return rule;				
			}
		}		
		return null;		
	}
	
	public static void setUser(User user){
		userManager.setUser(user);
	}
	
	public static User getUser(){
		return userManager.getUser();
	}

	public PhaseId getPhaseId() {
		return PhaseId.RENDER_RESPONSE;
	}
	
	private synchronized void initialize(FacesContext ctx){
		try {			
			try {
				userManager = (UserManager)Class.forName(ctx.getExternalContext().getInitParameter(USER_MANAGER)).newInstance();	
			} catch (Exception e) {
				userManager = (UserManager)Class.forName(DEFAULT_USER_MANAGER).newInstance();
			}
			logger.info("USER MANAGER LOADED-->"+userManager);
			String url = ctx.getExternalContext().getInitParameter(CONFIG);
			InputStream configStream = FacesContext.getCurrentInstance().getExternalContext().getResourceAsStream(url);			
			Digester digester = new Digester();			
			
			digester.addObjectCreate("access",Access.class);
			digester.addSetProperties("access","error","error");
			
			digester.addObjectCreate("access/rule",AccessRule.class);
			digester.addSetProperties("access/rule","name","name");
			digester.addSetProperties("access/rule","private","private");
			digester.addSetNext("access/rule","addRule");
			
			digester.addObjectCreate("access/rule/groups/group",AccessGroup.class);
			digester.addSetProperties("access/rule/groups/group","name","name");
			digester.addSetNext("access/rule/groups/group","addGroup");
			
			digester.addObjectCreate("access/rule/groups/group/rights/right",AccessRight.class);
			digester.addSetProperties("access/rule/groups/group/rights/right","name","name");
			digester.addSetNext("access/rule/groups/group/rights/right","addRight");
			
			access = ((Access)digester.parse(configStream));
						
			logger.info("ACCESS MANAGER INITIALIZED-->"+access);
		} catch (Exception e) {
			e.printStackTrace();
			access  = new Access();
			access.setRules(new ArrayList());			
		}finally{
			Collections.sort(access.getRules(),new RuleComparator());
		}
	}
	
	class RuleComparator implements Comparator{
		public int compare(Object arg0, Object arg1) {
			int result = -1;			
			int len0 = ((AccessRule)arg0).getName()==null?0:((AccessRule)arg0).getName().length();
			int len1 = ((AccessRule)arg1).getName()==null?0:((AccessRule)arg1).getName().length();
			if( len0 < len1 ){
				result = 1;
			}else if( len0 == len1 ){
				result = 0 ;
			}
			return result;
		}	
	}
}
